package tcln.gxtx.client.utils.date;

import java.text.SimpleDateFormat;
import java.util.Date;

import com.google.gwt.user.datepicker.client.CalendarUtil;

/**
 * Abstract class for an instant of date in GWT. This class provides default implementation and static methods using
 * {@link Date} class inside.
 * 
 * @author Truong Chau Lien Ngoc (lienngoc@gmail.com)
 */
public abstract class DefaultGwtDateInstant extends GwtDateInstant {

    /**
     * Returns a {@link GwtDate} representing current date.
     * 
     * @return
     */
    public static GwtDate today() {
        return new DefaultGwtDateImpl();
    }

    /**
     * Returns a {@link GwtDateTime} representing current date/time.
     * 
     * @return
     */
    public static GwtDateTime now() {
        return new DefaultGwtDateTimeImpl();
    }

    /**
     * Creates a new {@link GwtDate} from a Java date.
     * 
     * @param date
     *            Input date.
     * @return new {@link GwtDate}.
     * @throws NullPointerException
     *             if given date is <code>null</code>.
     */
    @SuppressWarnings("deprecation")
    public static GwtDate createDate(Date date) {
        return new DefaultGwtDateImpl(new Date(date.getYear(), date.getMonth(), date.getDate()));
    }

    /**
     * Creates a new {@link GwtDateTime} from a Java date.
     * 
     * @param date
     *            Input date.
     * @return new {@link GwtDateTime}.
     * @throws NullPointerException
     *             if given date is <code>null</code>.
     */
    @SuppressWarnings("deprecation")
    public static GwtDateTime createDateTime(Date date) {
        return new DefaultGwtDateTimeImpl(new Date(date.getYear(), date.getMonth(), date.getDate(), date.getHours(),
                date.getMinutes(), date.getSeconds()));
    }

    /**
     * Creates a new {@link GwtDateTime} from a Java date.
     * 
     * @param date
     *            Input date.
     * @param hour
     *            Hour, from 0 to 23.
     * @param minute
     *            Minute, from 0 to 59.
     * @param second
     *            Second, from 0 to 59.
     * @return new {@link GwtDateTime}.
     * @throws NullPointerException
     *             if given date is <code>null</code>.
     */
    @SuppressWarnings("deprecation")
    public static GwtDateTime createDateTime(Date date, int hour, int minute, int second) {
        return new DefaultGwtDateTimeImpl(new Date(date.getYear(), date.getMonth(), date.getDate(), hour, minute,
                second));
    }

    /**
     * Creates a new {@link GwtDate} from given (year, month, day) combination.
     * 
     * @param year
     *            Year, 4-digit.
     * @param month
     *            Month, from 1 to 12.
     * @param dayOfMonth
     *            Day of month, starting from 1.
     * @return
     */
    @SuppressWarnings("deprecation")
    public static GwtDate create(int year, int month, int dayOfMonth) {
        return new DefaultGwtDateImpl(new Date(year - 1900, month - 1, dayOfMonth));
    }

    /**
     * Creates a new {@link GwtDateTime} from given (year, month, day, hour, minute, second) combination.
     * 
     * @param year
     *            Year, 4-digit.
     * @param month
     *            Month, from 1 to 12.
     * @param dayOfMonth
     *            Day of month, starting from 1.
     * @param hour
     *            Hour, from 0 to 23.
     * @param minute
     *            Minute, from 0 to 59.
     * @param second
     *            Second, from 0 to 59.
     * @return
     */
    @SuppressWarnings("deprecation")
    public static GwtDateTime create(int year, int month, int dayOfMonth, int hour, int minute, int second) {
        return new DefaultGwtDateTimeImpl(new Date(year - 1900, month - 1, dayOfMonth, hour, minute, second));
    }

    protected Date innerDate;

    /**
     * Constructs with current date.
     */
    protected DefaultGwtDateInstant() {
        this(null);
    }

    /**
     * Constructs this date instant with a specific inner {@link Date}.
     * 
     * @param inner
     */
    protected DefaultGwtDateInstant(Date inner) {
        super();
        innerDate = inner != null ? inner : new Date();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((innerDate == null) ? 0 : innerDate.hashCode());
        return result;
    }

    /**
     * Compares current instant with another instant.
     * 
     * @param o
     *            Instant object to compare with this instant.
     * @return <b>0</b> for equation, <code>1</code> if this instant is after argument instant and <b>-1</b> for the
     *         reversed case.
     */
    public int compareTo(DefaultGwtDateInstant o) {
        long dis = innerDate.getTime() - o.innerDate.getTime();
        return dis > 0 ? 1 : (dis < 0 ? -1 : 0);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int compareTo(GwtDateInstant o) {
        if (o instanceof DefaultGwtDateInstant) {
            return compareTo((DefaultGwtDateInstant) o);
        }
        return 1;
    }

    /**
     * See documentation of {@link GwtDateTime}.
     * 
     * @return
     */
    @SuppressWarnings("deprecation")
    public int getHour() {
        return innerDate.getHours();
    }

    /**
     * See documentation of {@link GwtDateTime}.
     * 
     * @return
     */
    @SuppressWarnings("deprecation")
    public int getMinute() {
        return innerDate.getMinutes();
    }

    /**
     * See documentation of {@link GwtDateTime}.
     * 
     * @return
     */
    @SuppressWarnings("deprecation")
    public int getSecond() {
        return innerDate.getSeconds();
    }

    /**
     * See documentation of {@link GwtDateTime}.
     * 
     * @param hours
     */
    @SuppressWarnings("deprecation")
    public void addHour(int hours) {
        if (hours > 0) {
            innerDate.setHours(innerDate.getHours() - hours);
        }
    }

    /**
     * See documentation of {@link GwtDateTime}.
     * 
     * @param minutes
     */
    @SuppressWarnings("deprecation")
    public void addMinute(int minutes) {
        if (minutes > 0) {
            innerDate.setMinutes(innerDate.getMinutes() - minutes);
        }
    }

    /**
     * See documentation of {@link GwtDateTime}.
     * 
     * @param seconds
     */
    @SuppressWarnings("deprecation")
    public void addSecond(int seconds) {
        if (seconds > 0) {
            innerDate.setSeconds(innerDate.getSeconds() - seconds);
        }
    }

    /**
     * See documentation of {@link GwtDateTime}.
     * 
     * @param hours
     */
    @SuppressWarnings("deprecation")
    public void minusHour(int hours) {
        if (hours > 0) {
            innerDate.setHours(innerDate.getHours() + hours);
        }
    }

    /**
     * See documentation of {@link GwtDateTime}.
     * 
     * @param minutes
     */
    @SuppressWarnings("deprecation")
    public void minusMinute(int minutes) {
        if (minutes > 0) {
            innerDate.setMinutes(innerDate.getMinutes() + minutes);
        }
    }

    /**
     * See documentation of {@link GwtDateTime}.
     * 
     * @param seconds
     */
    @SuppressWarnings("deprecation")
    public void minusSecond(int seconds) {
        if (seconds > 0) {
            innerDate.setSeconds(innerDate.getSeconds() + seconds);
        }
    }

    /**
     * See documentation of {@link GwtDate}.
     * 
     * @return
     */
    public long getMilliseconds() {
        return innerDate.getTime();
    }

    /**
     * See documentation of {@link GwtDate}.
     * 
     * @return
     */
    @SuppressWarnings("deprecation")
    public int getDayOfWeek() {
        return innerDate.getDay();
    }

    /**
     * See documentation of {@link GwtDate}.
     * 
     * @return
     */
    @SuppressWarnings("deprecation")
    public int getDayOfMonth() {
        return innerDate.getDate();
    }

    /**
     * See documentation of {@link GwtDate}.
     * 
     * @return
     */
    @SuppressWarnings("deprecation")
    public int getMonth() {
        return innerDate.getMonth() + 1;
    }

    /**
     * See documentation of {@link GwtDate}.
     * 
     * @return
     */
    @SuppressWarnings("deprecation")
    public int getYear() {
        return innerDate.getYear() + 1900;
    }

    /**
     * See documentation of {@link GwtDate}.
     * 
     * @param days
     */
    public void addDayOfMonth(int days) {
        if (days > 0) {
            CalendarUtil.addDaysToDate(innerDate, days);
        }
    }

    /**
     * See documentation of {@link GwtDate}.
     * 
     * @param months
     */
    public void addMonth(int months) {
        if (months > 0) {
            CalendarUtil.addMonthsToDate(innerDate, months);
        }
    }

    /**
     * See documentation of {@link GwtDate}.
     * 
     * @param years
     */
    @SuppressWarnings("deprecation")
    public void addYear(int years) {
        if (years > 0) {
            innerDate.setYear(innerDate.getYear() + years);
        }
    }

    /**
     * See documentation of {@link GwtDate}.
     * 
     * @param days
     */
    public void minusDayOfMonth(int days) {
        if (days > 0) {
            CalendarUtil.addDaysToDate(innerDate, -days);
        }
    }

    /**
     * See documentation of {@link GwtDate}.
     * 
     * @param months
     */
    public void minusMonth(int months) {
        if (months > 0) {
            CalendarUtil.addMonthsToDate(innerDate, -months);
        }
    }

    /**
     * See documentation of {@link GwtDate}.
     * 
     * @param years
     */
    @SuppressWarnings("deprecation")
    public void minusYear(int years) {
        if (years > 0 && years < innerDate.getYear()) {
            innerDate.setYear(innerDate.getYear() - years);
        }
    }

    /**
     * See documentation of {@link GwtDate}.
     * 
     * @param pattern
     * @return
     */
    public String toString(String pattern) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
        return dateFormat.format(innerDate);
    }

    /**
     * See documentation of {@link GwtDate} and {@link GwtDateTime}.
     * 
     * @return
     */
    public Date toJavaDate() {
        return new Date(innerDate.getTime());
    }

    /**
     * Clear 'date' portions including <b>year</b>, <b>month</b> and <b>day-of-month</b> parts.
     */
    @SuppressWarnings("deprecation")
    protected void clearDatePortions() {
        int h = innerDate.getHours();
        int m = innerDate.getMinutes();
        int s = innerDate.getSeconds();
        innerDate.setTime(0);
        innerDate.setHours(h);
        innerDate.setMinutes(m);
        innerDate.setSeconds(s);
    }

    /**
     * Clear 'time' portions including <b>hour</b>, <b>minute</b> and <b>second</b> parts.
     */
    @SuppressWarnings("deprecation")
    protected void clearTimePortions() {
        int y = innerDate.getYear();
        int m = innerDate.getMonth();
        int d = innerDate.getDate();

        innerDate.setHours(0);
        innerDate.setMinutes(0);
        innerDate.setSeconds(0);

        innerDate.setYear(y);
        innerDate.setMonth(m);
        innerDate.setDate(d);
    }
}
